'use strict';
const Service = require('egg').Service;
const util = require('../lib/util.js');

class TortiousService extends Service {

  // 恢复删除
  async recoveryTortious(tort_word_id) {
    const { app, ctx } = this;
    const tortious = await app.model.TortWords.findAll({
      where: { tort_word_id },
      paranoid: false,
    });
    if (!(tortious && tortious.length > 0)) {
      return { errcode: app.ERRCODE.DATA_ERROR, message: `not fond tortiou by tort_word_id:${tort_word_id}` };
    }
    let transaction;
    try {
      // 建立事务对象
      transaction = await ctx.model.transaction();
      for (const tortiou of tortious) {
        if (tortiou.nice_cl) {
          for (const nice_cl of tortiou.nice_cl) {
            await app.defineWords(nice_cl).restore({
              where: { tort_word_id: tortiou.tort_word_id },
            }, { transaction });
          }
        }
        await tortiou.restore({}, { transaction });
      }
      // 提交事务
      await transaction.commit();
      return { errcode: app.ERRCODE.NORMAL, message: 'success' };
    } catch (err) {
      // 事务回滚
      await transaction.rollback();
      return { errcode: app.ERRCODE.DATABASE_ERROR, message: '操作失败', error: err.message };
    }

  }

  // 软删除
  async destroyTortious(tort_word_id) {
    const { app, ctx } = this;
    const tortious = await app.model.TortWords.findAll({
      where: { tort_word_id },
    });
    if (!(tortious && tortious.length > 0)) {
      return { errcode: app.ERRCODE.DATA_ERROR, message: `not fond tortiou by tort_word_id:${tort_word_id}` };
    }
    let transaction;
    try {
      // 建立事务对象
      transaction = await ctx.model.transaction();
      for (const tortiou of tortious) {
        if (tortiou.nice_cl) {
          for (const nice_cl of tortiou.nice_cl) {
            await app.defineWords(nice_cl).destroy({
              where: { tort_word_id: tortiou.tort_word_id },
            }, { transaction });
          }
        }
        await app.model.TortWords.destroy({ where: { tort_word_id } }, { transaction });
      }
      // 提交事务
      await transaction.commit();
      return { errcode: app.ERRCODE.NORMAL, message: 'success' };
    } catch (err) {
      // 事务回滚
      await transaction.rollback();
      return { errcode: app.ERRCODE.DATABASE_ERROR, message: '操作失败', error: err.message };
    }
  }


  async existsTortious(categorie_id) {
    return await this.app.defineWords(categorie_id).findOne().then(word => !word)
      .catch(() => true);
  }


  async tortiousList(options = {}) {
    const app = this.app;
    const Op = app.Sequelize.Op;
    const where = {};
    if (Number.isInteger(Number(options.last_sync_stime) || null)) {
      where['$tortWords.last_sync_time$'] = {
        [Op.gte]: Number(options.last_sync_stime),
      };
    }
    if (options.source) {
      where.source = options.source;
    }
    if (Number.isInteger(Number(options.last_sync_etime) || null)) {
      where['$tortWords.last_sync_time$'] = {
        [Op.lt]: Number(options.last_sync_etime),
      };
    }
    if (Number.isInteger(Number(options.updated_stime) || null)) {
      where.updated_at = {
        [Op.gte]: Number(options.updated_stime),
      };
    }
    if (Number.isInteger(Number(options.updated_etime) || null)) {
      where.updated_at = {
        [Op.lt]: Number(options.updated_etime),
      };
    }
    const tortWordList = await app.model.TortWords.findAndCountAll({
      attributes: [ 'tort_word_id', 'word', 'source', 'hoder', 'nice_cl', 'status', 'app_data', 'last_sync_time', 'updated_at' ],
      limit: options.page_size,
      offset: options.page_size * Math.max(0, (options.page - 1)),
      where,
      raw: true,
      paranoid: false,
    });
    const tort_word_id_set = new Set();
    for (const tortWord of tortWordList.rows) {
      tort_word_id_set.add(tortWord.tort_word_id);
    }
    const last_sync_time = Date.now();
    await app.model.TortWords.update({ last_sync_time }, {
      where: { tort_word_id: Array.from(tort_word_id_set) },
    });
    return { errcode: app.ERRCODE.NORMAL, message: 'success', page: options.page, page_size: options.page_size, ...tortWordList };
  }


  async showTortiousList(options = {}) {
    const app = this.app;
    const Op = app.Sequelize.Op;
    const where = {};
    if (options.source) {
      where.source = options.source;
    }
    if (Number.isInteger(Number(options.updated_stime) || null)) {
      where.updated_at = {
        [Op.gte]: Number(options.updated_stime),
      };
    }
    if (Number.isInteger(Number(options.updated_etime) || null)) {
      where.updated_at = {
        [Op.lt]: Number(options.updated_etime),
      };
    }
    if (options.word) {
      switch (options.word_mode) {
        case 2: // 精准多词搜索
          where.word = options.word;
          break;
        case 1: // 模糊搜索
        default:
          where.word = {
            [Op.like]: `%${options.word}%`,
          };
          break;
      }

    }
    // 按匹配度优先级排序
    // SELECT * FROM `tort_words` WHERE word LIKE '%A%' ORDER BY ( CASE WHEN word='A' THEN 1 WHEN word like 'A%' THEN 2 WHEN word like '%A%' THEN 3 WHEN word like '%A' THEN 4 ELSE 5 END )
    let tortWordList = { rows: [], count: 0 };
    if (options.category_id) {
      if (await this.existsTortious(options.category_id)) {
        return { errcode: app.ERRCODE.NORMAL, message: `nice_cl:${options.category_id} does not exist`, page: options.page, page_size: options.page_size, ...tortWordList };
      }
      tortWordList = await this.app.defineWords(options.category_id).findAndCountAll({
        attributes: [ 'tort_word_id', 'word', 'source', 'hoder', 'status', 'app_data', 'updated_at', 'deleted_at' ],
        limit: options.page_size,
        offset: options.page_size * Math.max(0, (options.page - 1)),
        where,
        order: [
          [ 'updated_at', 'desc' ],
        ],
        raw: true,
        paranoid: false,
      });
      for (const tortWord of tortWordList.rows) {
        tortWord.nice_cl = [ options.category_id ];
      }
    } else {
      tortWordList = await app.model.TortWords.findAndCountAll({
        attributes: [ 'tort_word_id', 'word', 'source', 'hoder', 'nice_cl', 'status', 'app_data', 'updated_at', 'deleted_at' ],
        limit: options.page_size,
        offset: options.page_size * Math.max(0, (options.page - 1)),
        where,
        order: [
          [ 'updated_at', 'desc' ],
        ],
        raw: true,
        paranoid: false,
      });
    }
    return { errcode: app.ERRCODE.NORMAL, message: 'success', page: options.page, page_size: options.page_size, ...tortWordList };
  }


  async organizeDocs(docs) {
    const result = [];
    for (const ele of docs) {
      if (ele.BRAND && ele.AD) {
        for (const brand of ele.BRAND) {
          result.push({
            BRAND: brand,
            SOURCE: ele.SOURCE.replace('TM', ''),
            HOL: ele.HOL ? ele.HOL.join(';') : '',
            NC: ele.NC,
            STATUS: ele.STATUS,
            app_data: Date.parse(ele.AD) - 28800000,
          });
        }
      }
    }
    return result;
  }


  async organizeDocsHishorys(docs) {
    const result = [];
    for (const ele of docs) {
      if (ele.BRAND) {
        for (const brand of ele.BRAND) {
          result.push({
            word: brand,
            source: ele.SOURCE.replace('TM', ''),
            hoder: ele.HOL ? ele.HOL.join(';') : '',
            nice_cl: ele.NC,
            status: ele.STATUS,
            app_data: ele.AD ? Date.parse(ele.AD) - 28800000 : null,
          });
        }
      }
    }
    return result;
  }

  async addTortDesign(tortious) {
    const app = this.app;
    await app.model.define('tort_designs', app.model.TortDesigns.tableAttributes).sync();
    await app.model.TortDesigns.findOrCreate({
      where: { regNo: tortious.regNo },
      paranoid: false,
      defaults: {
        regNo: tortious.regNo,
        holder: tortious.holder,
        locarno_cl: tortious.locarno_cl,
        ind_prod: tortious.ind_prod,
        designations: tortious.designations,
        image: tortious.image,
        regDate: tortious.regDate,
        last_crawler_time: Date.now(),
      },
    });
    // 分类插入 最新数据
    for (const lc of tortious.locarno_cl) {
      const i = tortious.locarno_cl.indexOf(lc);
      const lcn = lc.split('-')[0];
      await app.model.define(`tort_designs_${lcn}`, app.model.DesignTpls.tableAttributes).sync();
      await app.defineDesigns(lcn).create({
        regNo: tortious.regNo,
        regDate: tortious.regDate,
        last_crawler_time: Date.now(),
        image: tortious.image[i],
      });
      // await app.defineDesigns(lcn).findOrCreate({
      //   where: { image: tortious.image[i] },
      //   paranoid: false,
      //   defaults: {
      //     regNo: tortious.regNo,
      //     regDate: tortious.regDate,
      //     last_crawler_time: Date.now(),
      //     image: tortious.image[i],
      //   },
      // });
    }

  }

  async addTortious(tortious) {
    const app = this.app;
    const deleted_at = app.regExp.test(tortious.BRAND) ? Date.now() : null;
    const [ tortiousWord ] = await app.model.TortWords.findOrCreate({
      where: { word: tortious.BRAND, source: tortious.SOURCE },
      paranoid: false,
      defaults: {
        app_data: tortious.app_data,
        hoder: tortious.HOL,
        nice_cl: tortious.NC,
        status: tortious.STATUS,
        last_crawler_time: Date.now(),
        deletedAt: deleted_at,
      },
    });
    if ((tortious.SOURCE !== tortiousWord.source) || (tortious.app_data > tortiousWord.app_data)) { // 合并更新
      tortiousWord.last_crawler_time = Date.now();
      tortiousWord.app_data = tortious.app_data;
      tortiousWord.hoder = tortious.HOL;
      tortiousWord.source = tortious.SOURCE;
      tortiousWord.app_data = tortious.app_data;
      if (tortious.NC) {
        tortiousWord.nice_cl = Array.from(new Set([ ...(tortiousWord.nice_cl || []), ...tortious.NC ]));
      }
      tortiousWord.status = tortious.STATUS;
      await tortiousWord.save();
    }
    // 累加到历史表
    await app.model.TortWordHistories.create({
      tort_word_id: tortiousWord.tort_word_id,
      word: tortious.BRAND,
      source: tortious.SOURCE,
      hoder: tortious.HOL,
      nice_cl: tortious.NC,
      status: tortious.STATUS,
      app_data: tortious.app_data,
    });
    const ncTortious = [];
    if (tortious.NC && tortious.NC.length > 0) { // 有分类
      for (const nc of tortious.NC) {
        ncTortious.push(Object.assign({ nc }, tortious));
      }
    } else {
      ncTortious.push(Object.assign({ nc: '-0' }, tortious));
    }
    // 分类插入 最新数据
    for (const words of ncTortious) {
      await app.model.define(`tort_words_${words.nc}`, app.model.WordTpls.tableAttributes).sync();
      const [ ncWord ] = await app.defineWords(words.nc).findOrCreate({
        where: { word: tortious.BRAND, source: tortious.SOURCE },
        paranoid: false,
        defaults: {
          tort_word_id: tortiousWord.tort_word_id,
          app_data: tortious.app_data,
          hoder: tortious.HOL,
          // nice_cl: tortious.NC,
          status: tortious.STATUS,
          // last_crawler_time: Date.now(),
          deletedAt: deleted_at,
        },
      });
      if ((tortious.SOURCE !== ncWord.source) || (tortious.app_data > ncWord.app_data)) {
        // ncWord.last_crawler_time = Date.now();
        ncWord.app_data = tortious.app_data;
        ncWord.source = tortious.SOURCE;
        ncWord.hoder = tortious.HOL;
        ncWord.status = tortious.STATUS;
        await tortiousWord.save();
      }
    }
  }

  // 爬取删减
  async delTortious(tortious) {
    const app = this.app;
    const tortiousWord = await app.model.TortWords.findOne({
      where: { word: tortious.BRAND, source: tortious.SOURCE },
      paranoid: false,
    });
    if (tortiousWord) {
      if (tortious.app_data > tortiousWord.app_data) { // 删减更新
        tortiousWord.last_crawler_time = Date.now();
        tortiousWord.app_data = tortious.app_data;
        tortiousWord.hoder = tortious.HOL;
        if (tortious.NC) {
          const del_nice_cl = (tortiousWord.nice_cl || []).filter(ele => tortious.NC.includes(ele));
          tortiousWord.nice_cl = (tortiousWord.nice_cl || []).filter(ele => !tortious.NC.includes(ele));
          for (const nc of del_nice_cl) {
            await app.model.define(`tort_words_${nc}`, app.model.WordTpls.tableAttributes).sync();
            await app.defineWords(nc).destroy({
              where: { word: tortious.BRAND, source: tortious.SOURCE },
              force: true,
            });
          }
        } else {
          await app.model.define('tort_words_-0', app.model.WordTpls.tableAttributes).sync();
          await app.defineWords('-0').destroy({
            where: { word: tortious.BRAND, source: tortious.SOURCE },
            force: true,
          });
        }
        await tortiousWord.save();
      }
      // 累加到历史表
      await app.model.TortWordHistories.create({
        tort_word_id: tortiousWord.tort_word_id,
        word: tortious.BRAND,
        source: tortious.SOURCE,
        hoder: tortious.HOL,
        nice_cl: tortious.NC,
        status: tortious.STATUS,
        app_data: tortious.app_data,
      });
    }
  }

  async changeTortious(response, data = '') {
    const app = this.app;
    const docs = await this.organizeDocs(response.docs);
    const hishoryDocs = await this.organizeDocsHishorys(response.docs);
    for (const tortious of docs) {
      switch (tortious.STATUS) {
        case 'DEL':
          await this.delTortious(tortious);
          break;
        case 'ACT':
        default:
          await this.addTortious(tortious);
          break;
      }
    }
    if (hishoryDocs.length > 0) {
      await app.model.TortWordAllHistories.bulkCreate(hishoryDocs);
    }
    if (data) {
      await util.saveDataStatus(response, data);
    }
    return { errcode: app.ERRCODE.NORMAL, message: 'success' };
  }

  async latestTortiousInfo() {
    const app = this.app;
    let [ last_sync_time, app_data, count ] = await Promise.all([ app.model.TortWords.max('last_sync_time'), app.model.TortWords.max('app_data'), app.model.TortWords.count() ]);
    if (last_sync_time) {
      last_sync_time = last_sync_time.getTime();
    }
    if (app_data) {
      app_data = app_data.getTime();
    }
    const trafficStatistics = await app.redis.get('traffic-statistics') || 0;
    return {
      errcode: app.ERRCODE.NORMAL,
      message: 'success',
      data: { last_sync_time, app_data, count, trafficStatistics: `${(trafficStatistics / 1024 / 1024).toFixed(4)} MB` },
    };
  }

  async getConifg(key = '', value = {}) {
    return await this.app.model.Configs1.findOrCreate({ where: { key }, defaults: { key, value, status: 1 } }).then(async config_list => {
      return config_list[0];
    });
  }
}
module.exports = TortiousService;
